----------------------各系统模块加载工具----------------------
_G.g_envs = _G.g_envs or {} -- 全局模块环境缓存
local IMPORT_MODULE_ENV = _G.g_envs

--加载模块文件到沙盒环境（新环境继承 _G 的全局变量）
local function loadmodule(fileName, env)
    if not env then
        env = {}
        setmetatable(env, { __index = _G }) -- 创建沙盒环境
    end
    local retCode, chunk, errMsg = pcall(loadfile, fileName, nil, env)
    if not retCode then return false, chunk end -- 加载错误
    if not chunk then return false, errMsg end  -- 文件不存在
    chunk()                                     -- 执行模块代码
    return true, env
end

-----------------------------模块加载逻辑-----------------------------
ALL_CLASS = ALL_CLASS or {}                -- 全局类注册表
setmetatable(ALL_CLASS, { __mode = "kv" }) -- 弱引用防止内存泄漏

--ready 在 init 之后,在 update 之前会被调用
local function ready(mod)
    if mod.module_ready then
        mod:module_ready()
    end
end

local function init(mod)
    -- 注册模块中定义的类
    for k, v in pairs(mod) do
        if type(v) == "table" and rawget(v, "_class_name") then
            if ALL_CLASS[v._class_name] then
                ALL_CLASS[v._class_name] = nil -- 清除旧类
                print("repeat regist class:", v._class_name)
            end
            ALL_CLASS[v._class_name] = v -- 注册新类
        end
    end
    -- 调用模块初始化函数
    if mod.module_init then
        mod:module_init()
    end
end

local function update(mod)
    if mod.module_update then
        mod:module_update()
    end
end


-----------------------------模块热更逻辑-----------------------------
--注意:这是类对象而不是类实例的更新
--类的旧的函数不能删除只能更新
--类的需要更新的所有值的类型不能修改
--新增的函数和值可以更新
local function replace_obj(dObj, sObj, depth)
    -- 递归更新类成员（最大深度20）
    depth = depth + 1
    assert(depth <= 20, "_replace_obj_: too deep")
    for k, old_v in pairs(dObj) do
        local new_v = rawget(sObj, k)
        if new_v then --没被删除
            local told = type(old_v)
            local tnew = type(new_v)
            if told == tnew then -- 类型一致才处理
                if told == "table" then
                    --如果是 cls.__mt,则类实例的元表还是用旧的,因为类对象都不变.
                    --其实新的env的类对象并没有 __mt,因为还没New()过实例,但其他table值可能引用到别的cls
                    if k == "__index" then
                        -- 跳过元表索引，避免破坏类实例
                    else
                        replace_obj(old_v, new_v, depth) -- 递归合并子表
                    end
                elseif told == "function" then
                    dObj[k] = new_v -- 函数直接替换为新版本
                end
                -- 其他类型（number/string等）保持不变
            end
        end
    end

    -- 添加新增属性（如新函数、子表）
    for k, new_v in pairs(sObj) do
        if not rawget(dObj, k) then -- 旧对象不存在该属性
            dObj[k] = new_v
        end
    end

    -- 继承新对象的元表（如父类引用）
    setmetatable(dObj, getmetatable(sObj))
end

-- 合并新旧环境函数
local function replace_env(newEnv, oldCache)
    for k, old_v in pairs(oldCache) do
        local new_v = rawget(newEnv, k) -- env的元表__index=_G,env的值都用rawget
        rawset(newEnv, k, old_v)        -- 默认为不替换
        if new_v then                   -- 新的env有对应的值才做替换
            local told = type(old_v)
            local tnew = type(new_v)
            if told == tnew then            --相同类型的值才作替换,主要是替换 非类对象的同名函数 和 类对象
                if told == "function" then  --非类对象的函数必须用新的
                    newEnv[k] = new_v
                elseif told == "table" then --table引用,对象类型的要恢复它的元素的引用有效
                    local newClsName = rawget(new_v, "_class_name")
                    local oldClsName = rawget(old_v, "_class_name")
                    if oldClsName and oldClsName == newClsName then --类对象内部替换
                        --类对象的实例化 __mt 不变
                        local old_mt = old_v.__mt
                        local index
                        if old_mt then index = old_mt.__index end --如果类未曾实例化过,就么有 __mt

                        replace_obj(old_v, new_v, 0)

                        old_v.__mt = old_mt
                        if old_mt then old_mt.__index = index end
                    end
                    --类对象的元表(不在同一文件下声明的条件下)不会修改,但不能保证父类也被热更的情况
                    --最好还是替换为新的env的元表
                    --即便new_v是普通的table,如果它有元表,也更新为新的env的(注意一些特殊情况,比如元表索引到模块对象)
                    local mt = getmetatable(new_v)
                    if mt then setmetatable(old_v, mt) end
                end
            else
                -- 类型不同则跳过（如变量从number变为table，保留旧值）
                -- 热更只是更新逻辑代码,而不应该改变模块的变量数值
            end
        end
    end
end

-- 同一个模块文件里,父类和子类不能同时出现
-- 否则父子类在同一模块会同时被更新，如果顺序不对就会出错：
-- 子类先更新 → 继承旧父类
-- 父类后更新 → 子类元表指向过时的父类
local function parentChildClsCheck(modEnv)
    -- 收集所有类对象
    local allcls = {}
    for k, v in pairs(modEnv) do
        if type(v) == "table" and rawget(v, "_class_name") then
            allcls[v] = k
        end
    end

    -- 检查类继承关系
    for _, v in pairs(modEnv) do
        if type(v) == "table" and rawget(v, "_class_name") then
            local mt = getmetatable(v)
            if mt and mt.__index and allcls[mt.__index] then
                -- 禁止父类和子类定义在同一模块（避免热更时元表冲突）
                return nil,
                    "父类和子类不能定义在同一模块: " ..
                    allcls[mt.__index] .. ":" .. v._class_name
            end
        end
    end
    return true
end

-- 安全设置函数环境
local function setfenv_safe(fn, env)
    local i = 1
    while true do
        local name, _ = debug.getupvalue(fn, i)
        if not name then break end
        if name == "_ENV" then
            debug.setupvalue(fn, i, env)
            break
        end
        i = i + 1
    end
    return fn
end

-- 模块实际内部加载逻辑
local function __internal_import(pathFile, modName, reload)
    -- 已存在且不能重新加载就直接返回
    local oldEnv = IMPORT_MODULE_ENV[pathFile]
    if oldEnv and not reload then return oldEnv end

    -- 首次加载
    if not oldEnv then
        if reload then return true, "只能热更已经加载的模块" end
        assert(modName, pathFile .. "必须要有模块名称")
        local ret, newEnv = loadmodule(pathFile)
        if not ret then
            print(string.format("[__internal_import] 内部加载模块失败: %s: %s", pathFile, newEnv))
            return
        end

        -- 执行成功得到环境后检查是否有父子类冲突
        local ret, errMsg = parentChildClsCheck(newEnv)
        if not ret then return ret, errMsg end

        -- 开始模块初始化工作
        init(newEnv)  -- init回调
        newEnv._mod_name_ = modName
        ready(newEnv) -- ready回调

        -- 存入缓存
        IMPORT_MODULE_ENV[pathFile] = newEnv
        assert(not _G[modName]) -- 确保模块不重复
        _G[modName] = newEnv    -- 注入全局空间
        return newEnv
    end

    -- 非首次加载走热更
    if not (reload and oldEnv) then return end
    local chunk, errMsg = loadfile(pathFile)
    if not chunk then return nil, errMsg end

    --oldEnv的mt要先去掉,不然chunk()会有很多新增字段
    local oldEnv_mt = getmetatable(oldEnv)
    local oldEnv_newindex = oldEnv_mt.__newindex
    oldEnv_mt.__newindex = nil
    local oldModName = oldEnv._mod_name_
    assert(oldModName)

    -- 备份旧环境并进行回滚准备
    local oldCache = {}
    for k, v in pairs(oldEnv) do
        oldCache[k] = v
        oldEnv[k] = nil
    end
    local function rollback()
        for k, v in pairs(oldEnv) do --清空所有key
            oldEnv[k] = nil
        end
        for k, v in pairs(oldCache) do --恢复所有旧的key
            oldEnv[k] = v
        end
        oldEnv_mt.__newindex = oldEnv_newindex
    end

    -- 重新在旧环境执行模块（覆盖旧函数/类定义）
    local success, err = pcall(function()
        chunk = setfenv_safe(chunk, oldEnv)
    end)
    if not success then
        rollback() -- 设置旧环境失败需要回滚
        print(string.format("[__internal_import] setfenv failed: %s: %s", pathFile, err))
        return
    end
    chunk() -- 执行开始覆盖

    -- 热更后安全检查
    local newEnv = oldEnv
    local ret, errMsg = parentChildClsCheck(newEnv)
    if not ret then
        rollback() -- 热更后有父子类冲突需要回滚
        return ret, errMsg
    end
    rawset(newEnv, "_mod_name_", oldModName)

    -- 合并新旧环境
    ret, errMsg = pcall(function()
        replace_env(newEnv, oldCache)
    end)
    if not ret then
        rollback() -- 合并环境失败需要回滚
        return ret, errMsg
    end

    -- 热更成功
    ready(newEnv)  -- 热更新就绪
    update(newEnv) -- 触发更新
    return newEnv
end

-- 模块导入入口
function _import(pathFile, modName, reload)
    pathFile = _LOGIC_DIR .. pathFile -- 补全路径
    local mod, errMsg = __internal_import(pathFile, modName, reload)
    if not mod then
        print(string.format("[_import]: 模块导入失败, pathFile=%s, errMsg=%s", pathFile, errMsg or "nil"))
        return
    else
        if reload then
            print(string.format("[_import]: 模块热更成功, pathFile=%s, errMsg=%s", pathFile, errMsg or "nil"))
        end
    end
    return mod
end

-- 热更新入口
function update_module(pathFile)
    -- 如果在检查禁止更新列表中就不热更
    if GAME_FORBID_UPDATE_LIST[pathFile] then
        print(string.format("[update_module]: 禁止热更模块: %s", pathFile))
        return false
    end

    -- 特殊文件直接重载
    for _, v in ipairs(GAME_DOFILE_LIST) do
        if pathFile == v then
            _do_file(pathFile) -- 直接执行
            return true
        end
    end

    --只能热更已经加载过的
    local mod = _import(pathFile, nil, true)
    if not mod then return end
    return true
end
